import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.util.List;
import java.util.Map;

import com.speljohan.rsbot.event.events.ServerMessageEvent;
import com.speljohan.rsbot.event.listeners.PaintListener;
import com.speljohan.rsbot.event.listeners.ServerMessageListener;
import com.speljohan.rsbot.script.Constants;
import com.speljohan.rsbot.script.Script;
import com.speljohan.rsbot.script.wrappers.RSInterface;
import com.speljohan.rsbot.script.wrappers.RSItem;
import com.speljohan.rsbot.script.wrappers.RSNPC;
import com.speljohan.rsbot.script.wrappers.RSTile;

public class AutoHighAlch extends Script implements PaintListener,
		ServerMessageListener {

	private int[] alchItem;

	private final int natureRunes = 561;
	private final int alchCastXP = 65;
	private int alchsCast = 0;
	private int startLevel = 0;
	private int startXP = 0;
	private int findFails = 0;
	private int paintType = 0;
	private boolean lostCityMode = false;
	private boolean dontContinue = false;
	private boolean levelJustGrown = false;
	private boolean interfaceFailed = false;
	private boolean movedTowards = false;
	long startTime = System.currentTimeMillis();
	private final Color BG = new Color(0, 0, 0, 75);

	private Color PERCBAR = new Color(0, 255, 0, 100);
	private final Color RED = new Color(75, 0, 0, 75);

	public AutoHighAlch() {
		setParameter("name", "AutoHighAlch");
		setParameter("version", 1.57);
		setParameter("author", "Jacmob");
		setParameter("category", "Magic");
		setParameter(
				"description",
				"<html>\n"
						+ "<head></head>\n"
						+ "<body style=\"font-family: Arial\">\n"
						+ "<center>"
						+ "<h2><span style=\"color: #AA0000\">High</span> <span style=\"color: #FF9933\">Level</span> <span style=\"color: #FFDD00\">Alchemy</span></h2>"
						+ "</center>\n"
						+ "<p\n"
						+ "<b>Author:</b> "
						+ getAuthor()
						+ "<p>\n"
						+ "<b>Version:</b> "
						+ getVersion()
						+ "\n<p>"
						+ "For more information about this script <a href= \"http://www.rsbot.org/vb/showthread.php?t=19534\">go to the forum thread</a>."
						+ "<p>\n"
						+ "<b>Features:</b>"
						+ "<ul><li>Any Item Alching</li><li>Anti-Ban</li><li>Painted Progress Info</li><li>Auto-Logout When Out Of Supplies</li><li>Zanaris Ruby Ring Purchasing and Alching</li></ul>"
						+ "<b>Alch Item IDs:</b>"
						+ "<p><input type=\"text\" name=\"alchItem\" value=\"856\" size=20 /><select name=\"paint\"><option>Large Paint</option><option>Small Paint</option><option>No Paint</option></select><br/>&nbsp;<strong><small>Default Is Noted Yew Longbow. Separate Multiple IDs With Commas. Enter \"zanaris\" To Alch Lost City Ruby Rings.</small></strong></p>"
						+ "</body>\n" + "</html>");
	}

	private void bottomPaint(final Graphics g) {
		long millis = System.currentTimeMillis() - startTime;
		final long hours = millis / (1000 * 60 * 60);
		millis -= hours * 1000 * 60 * 60;
		final long minutes = millis / (1000 * 60);
		millis -= minutes * 1000 * 60;
		final long seconds = millis / 1000;

		if (startLevel == 0) {
			startLevel = skills.getCurrentSkillLevel(Constants.STAT_MAGIC);
			startXP = skills.getCurrentSkillExp(Constants.STAT_MAGIC);
		}

		alchsCast = (skills.getCurrentSkillExp(Constants.STAT_MAGIC) - startXP)
				/ alchCastXP;

		g.setColor(BG);
		g.fill3DRect(3, 250, 514, 88, true);

		if (seconds != 0) {
			g.setColor(RED);
			g.fill3DRect(4, 235, 513, 15, true);
			g.setColor(PERCBAR);
			g.fill3DRect(4, 236, skills
					.getPercentToNextLevel(Constants.STAT_MAGIC) * 513 / 100,
					13, true);
		}

		g.setColor(Color.WHITE);

		g.setFont(new Font("SansSerif", 1, 12));
		g.drawString("Alched: " + alchsCast + " items", 9, 270);

		g.drawString("Next Level: "
				+ (int) Math.ceil((float) skills
						.getXPToNextLevel(Constants.STAT_MAGIC)
						/ (float) alchCastXP) + " casts", 9, 290);

		g.drawString("Current Magic Level: "
				+ skills.getCurrentSkillLevel(Constants.STAT_MAGIC), 9, 310);

		if (skills.getCurrentSkillLevel(Constants.STAT_MAGIC) - startLevel == 1) {
			g
					.drawString(
							"Gained: "
									+ alchsCast
									* 65
									+ " experience ("
									+ (skills
											.getCurrentSkillLevel(Constants.STAT_MAGIC) - startLevel)
									+ " level" + ")", 9, 330);
		} else {
			g
					.drawString(
							"Gained: "
									+ alchsCast
									* 65
									+ " experience ("
									+ (skills
											.getCurrentSkillLevel(Constants.STAT_MAGIC) - startLevel)
									+ " levels" + ")", 9, 330);
		}

		g.setColor(Color.CYAN);

		g.drawString("Runtime: " + hours + " hrs " + minutes + " min "
				+ seconds + " sec", 325, 270);
		if (lostCityMode) {
			g.drawString("Buying Zanaris Market Rings", 325, 290);
		}
	}

	public void castAlchSpell() {

		final int GambleInt = random(1, 300);

		final int spellX = RSInterface.getInterface(192).getChild(34)
				.getAbsoluteX()
				+ random(7, 16);
		final int spellY = RSInterface.getInterface(192).getChild(34)
				.getAbsoluteY()
				+ random(7, 16);

		if (GambleInt == 6) {
			moveMouse(520 + random(0, 230), 130 + random(0, 100));
			wait(random(100, 800));
		}
		if (GambleInt <= 3) {
			openTab(Constants.TAB_MAGIC);
			moveMouse(spellX, spellY);
			wait(random(950, 1300));
			clickMouse(true);
		} else if (GambleInt == 4) {
			moveCamera();
			openTab(Constants.TAB_MAGIC);
			wait(random(100, 200));
			clickMouse(spellX, spellY, true);
		} else if (GambleInt == 5 && getCurrentTab() != Constants.TAB_STATS) {
			if (random(1, 2) == 2) {
				openTab(Constants.TAB_MAGIC);
			}
			openTab(Constants.TAB_STATS);
			wait(random(1, 20));
			moveMouse(567 + random(0, 25), 393 + random(0, 14));
			wait(random(1100, 1800));
			openTab(Constants.TAB_MAGIC);
			clickMouse(spellX, spellY, true);
		} else {
			openTab(Constants.TAB_MAGIC);
			wait(random(650, 880));
			clickMouse(spellX, spellY, true);
		}

		wait(random(20, 100));

	}

	public void doAlch() {
		Point alchItemPos = getAlchItemPos();
		if (alchItemPos == null) {
			log("No Alchemy Items in Inventory - Shutting Down in 5 Seconds");
			wait(random(4500, 5500));
			stopAllScripts();
		}
		castAlchSpell();
		// openTab(TAB_INVENTORY);
		alchItemPos = getAlchItemPos();
		clickMouse(alchItemPos, true);
	}

	public void doLostCity() {
		try {
			Point alchItemPos = getAlchItemPos();
			if (alchItemPos == null) {
				if (interfaceFailed && findFails == 0) {
					log("Trade Failed - Moved To Irksol");
				} else if (findFails == 0) {
					log("No Alchemy Items In Inventory - Buying More");
				} else if (findFails == 1) {
					log("Missed Irksol - Trying Again");
				}
				wait(random(200, 600));
				if (random(1, 3) == 1) {
					openTab(Constants.TAB_INVENTORY);
				}
				wait(random(200, 600));
				final boolean traded = tradeFairy();
				if (!traded) {
					findFails++;
					if (findFails >= 25) {
						log("Unable To Trade Irksol - Shutting Down in 10 Seconds");
						wait(random(9000, 10000));
						stopAllScripts();
					} else if (findFails > 1) {
						moveCamera();
						if (findFails > 3) {
							if (getNearestNPCByName("Irksol") == null) {
								walkTileMM(getMyPlayer().getLocation()
										.randomizeTile(4, 4));
							} else {
								walkTileMM(getNearestNPCByName("Irksol")
										.getLocation().randomizeTile(3, 3));
							}
							wait(random(1000, 2000));
						}
						wait(random(300, 500));
					}
					return;
				}
				findFails = 0;
				while (!RSInterface.getInterface(620).isValid()) {
					wait(100);
					findFails++;
					if (findFails >= 50) {
						if (interfaceFailed == false) {
							interfaceFailed = true;
							findFails = 0;
							return;
						}
						log("Irksol Interface Not Found After 10 Seconds - Shutting Down in 10 Seconds");
						wait(random(9000, 10000));
						stopAllScripts();
					}
				}
				findFails = 0;
				interfaceFailed = false;
				movedTowards = false;
				wait(random(300, 600));
				if (random(1, 5) == 2) {
					wait(random(400, 500));
				}
				final int randY = 107 + random(-5, 5);
				clickMouse(78 + random(-5, 5), randY, false);
				wait(random(20, 100));
				clickMouse(60 + random(2, 35), randY + 88 + random(-2, 2), true);
				wait(random(930, 1100));
				if (random(1, 5) == 2) {
					wait(random(1100, 1500));
				}
				if (dontContinue == true) {
					dontContinue = false;
					clickMouse(80 + random(-10, +10), 75 + random(-3, +3), true);
					wait(random(500, 900));
					clickMouse(486 + random(-2, 2), 42 + random(-2, 2), true);
					return;
				}
				sendText(Integer.toString(getFreeInvSpaceCount()), true);
				wait(random(80, 800));
				clickMouse(486 + random(-2, 2), 42 + random(-2, 2), true);
				wait(random(400, 2000));
				if (random(1, 4) == 1) {
					walkTileMM(getNearestNPCByName("Irksol").getLocation()
							.randomizeTile(3, 3));
					wait(random(100, 400));
				}
			} else if (getInventoryCount(natureRunes) != 0) {
				castAlchSpell();
				alchItemPos = getAlchItemPos();
				clickMouse(alchItemPos, true);
			}
		} catch (final Exception e) {

		}
	}

	public Point getAlchItemPos() {
		final RSItem[] iItems = getInventoryItems();
		for (final int element : alchItem) {
			final Point nip = getNearInvItemPoint(element);
			if (nip != null) {
				return nip;
			}
			for (int j = 0; j < iItems.length; j++) {
				if (iItems[j].getID() == element) {
					return randomiseInventoryItemPoint(getInventoryItemPoint(j));
				}
			}
		}
		return null;
	}

	private String getFormattedTime(final long timeMillis) {
		long millis = timeMillis;
		final long seconds2 = millis / 1000;
		final long hours = millis / (1000 * 60 * 60);
		millis -= hours * 1000 * 60 * 60;
		final long minutes = millis / (1000 * 60);
		millis -= minutes * 1000 * 60;
		final long seconds = millis / 1000;
		String hoursString = "";
		String minutesString = "";
		String secondsString = seconds + "";
		String type = "seconds";

		if (minutes > 0) {
			minutesString = minutes + ":";
			type = "minutes";
		} else if (hours > 0 && seconds2 > 0) {
			minutesString = "0:";
		}
		if (hours > 0) {
			hoursString = hours + ":";
			type = "hours";
		}
		if (minutes < 10 && type != "seconds") {
			minutesString = "0" + minutesString;
		}
		if (hours < 10 && type == "hours") {
			hoursString = "0" + hoursString;
		}
		if (seconds < 10 && type != "seconds") {
			secondsString = "0" + secondsString;
		}

		return hoursString + minutesString + secondsString + " " + type;
	}

	private int getFreeInvSpaceCount() {
		int invCount = 0;
		for (int i = 0; i < getInventoryArray().length; i++) {
			if (getInventoryArray()[i] != -1) {
				invCount++;
			}
		}
		invCount = 28 - invCount;
		if (invCount > getInventoryCount(natureRunes)) {
			invCount = getInventoryCount(natureRunes);
		}
		return invCount;
	}

	private Point getNearInvItemPoint(final int itemID) {
		final RSItem[] iItems = getInventoryItems();
		final int spellX = RSInterface.getInterface(192).getChild(34)
				.getAbsoluteX()
				+ random(7, 16);
		final int spellY = RSInterface.getInterface(192).getChild(34)
				.getAbsoluteY()
				+ random(7, 16);

		final int col = (int) Math.floor((spellX - 559) / 42);
		final int row = (int) Math.floor((spellY - 210) / 36);

		if (col >= 0 && col <= 3 && row >= 0 && row <= 6) {
			if (iItems[(row * 4 + col)].getID() == itemID) {
				return randomiseInventoryItemPoint(getInventoryItemPoint((row * 4 + col)));
			}
		}

		return null;
	}

	private void logInfo() {
		long millis = System.currentTimeMillis() - startTime;
		final long hours = millis / (1000 * 60 * 60);
		millis -= hours * 1000 * 60 * 60;
		final long minutes = millis / (1000 * 60);
		millis -= minutes * 1000 * 60;
		final long seconds = millis / 1000;

		log(skills.getCurrentSkillExp(Constants.STAT_MAGIC) - startXP
				+ " Magic XP Gained in " + hours + " hrs " + minutes + " min "
				+ seconds + " sec (" + alchsCast + " Alchs Cast)");
	}

	public int loop() {

		if (levelJustGrown) {
			levelJustGrown = false;
			openTab(Constants.TAB_STATS);
			wait(random(1, 20));
			moveMouse(567 + random(0, 25), 393 + random(0, 14));
			wait(random(900, 1300));
			clickMouse(true);
			wait(random(1000, 5000));
			clickMouse(449 + random(-2, 2), 51 + random(-2, 2), true);
		}

		if (getInventoryCount(natureRunes) == 0) {
			log("No Nature Runes in Inventory - Shutting Down in 10 Seconds");
			wait(random(9000, 10000));
			stopAllScripts();
		}
		if (lostCityMode) {
			doLostCity();
		} else {
			doAlch();
		}
		return random(700, 1300);
	}

	private void miniPaint(final Graphics g) {
		final int x = 312;
		int y = 4;

		if (startLevel == 0) {
			startLevel = skills.getCurrentSkillLevel(Constants.STAT_MAGIC);
			startXP = skills.getCurrentSkillExp(Constants.STAT_MAGIC);
		}

		final int levelsGained = skills.getRealSkillLevel(Constants.STAT_MAGIC)
				- skills.getLvlByExp((int) startXP);
		final long runSeconds = (System.currentTimeMillis() - startTime) / 1000;

		g.setColor(BG);
		if (runSeconds != 0) {
			g.fill3DRect(x - 6, y, 211, 146, true);
		} else {
			g.fill3DRect(x - 6, y, 211, 105, true);
		}

		g.setColor(Color.WHITE);
		g.setFont(new Font("SansSerif", 1, 12));

		String lstCty = "";

		if (lostCityMode) {
			lstCty = " [Z]";
		}

		g.drawString(getName() + " v" + getVersion() + lstCty, x, y += 17);
		g.drawString(getName() + " v" + getVersion(), x, y);
		g.drawString("Run Time: "
				+ getFormattedTime(System.currentTimeMillis() - startTime), x,
				y += 20);

		if (levelsGained < 0) {
			startXP = skills.getCurrentSkillExp(Constants.STAT_MAGIC);
		} else if (levelsGained == 1) {
			g
					.drawString(
							"Gained: "
									+ (skills
											.getCurrentSkillExp(Constants.STAT_MAGIC) - startXP)
									+ " XP (" + levelsGained + " lvl)", x,
							y += 20);
		} else {
			g
					.drawString(
							"Gained: "
									+ (skills
											.getCurrentSkillExp(Constants.STAT_MAGIC) - startXP)
									+ " XP (" + levelsGained + " lvls)", x,
							y += 20);
		}

		/*
		 * if (runSeconds > 0) { g.drawString("Averaging: " +
		 * ((skills.getCurrentSkillExp(STAT_MAGIC)-startXP) * 3600 / runSeconds)
		 * + " XP/hr", x, y += 20); }
		 */
		g.drawString("Alched: " + alchsCast + " items", x, y += 20);

		g.drawString("Current level: "
				+ skills.getRealSkillLevel(Constants.STAT_MAGIC), x, y += 20);
		g.drawString("Next level: "
				+ (int) Math.ceil((float) skills
						.getXPToNextLevel(Constants.STAT_MAGIC)
						/ (float) alchCastXP) + " casts", x, y += 20);
		if (runSeconds != 0) {
			g.setColor(RED);
			g.fill3DRect(x, y += 9, 200, 13, true);
			PERCBAR = new Color((int) (255 - Math.floor(2.55 * (double) skills
					.getPercentToNextLevel(Constants.STAT_MAGIC))), 255, 0, 100);
			g.setColor(PERCBAR);
			g.fill3DRect(x, y, skills
					.getPercentToNextLevel(Constants.STAT_MAGIC) * 2, 13, true);
		}
	}

	public void moveCamera() {
		final int r = random(0, 359);
		setCameraRotation(r);
	}

	public void onFinish() {
		logInfo();
	}

	public void onRepaint(final Graphics g) {
		if (isLoggedIn()) {
			if (paintType == 1) {
				miniPaint(g);
			} else if (paintType == 0) {
				bottomPaint(g);
			}
		}
	}

	@Override
	public boolean onStart(final Map<String, String> args) {
		if (args.get("alchItem").equals("")) {
			alchItem = new int[] { 856 };
		} else if (args.get("alchItem").toLowerCase().equals("zanaris")) {
			alchItem = new int[] { 1641 };
			lostCityMode = true;
		} else {
			final String text = args.get("alchItem").replaceAll(" ", "");
			try {
				final String[] strInts = text.split(",");
				alchItem = new int[strInts.length];
				for (int i = 0; i < alchItem.length; i++) {
					alchItem[i] = Integer.parseInt(strInts[i]);
				}
			} catch (final Exception e) {
				System.out.print("AlchItems Input Parse Error: " + e);
			}
		}
		log(args.get("paint"));
		if (args.get("paint").equals("Small Paint")) {
			paintType = 1;
		} else if (args.get("paint").equals("No Paint")) {
			paintType = 2;
		}

		if (lostCityMode) {
			log("Alching Zanaris Ruby Rings");
		} else if (alchItem.length > 1) {
			log("Alching " + alchItem.length + " Item IDs");
		} else if (alchItem[0] == 856) {
			log("Alching Noted Yew Longbows");
		} else {
			log("Alching Item ID " + alchItem[0]);
		}

		openTab(Constants.TAB_MAGIC);
		wait(random(400, 500));

		return true;
	}

	public void serverMessageRecieved(final ServerMessageEvent e) {
		final String messageEvent = e.getMessage();
		if (messageEvent.contains("You don't have enough coins")) {
			log("You Do Not Have Enough Coins - Shutting Down");
			stopAllScripts();
		} else if (messageEvent.contains("advanced a Magic level")) {
			levelJustGrown = true;
		} else if (messageEvent.contains("There is no player stock")) {
			dontContinue = true;
		} else if (messageEvent.contains("You do not have enough Fire Runes")) {
			log("No Fire Runes - Shutting Down in 10 Seconds");
			wait(random(9000, 10000));
			stopAllScripts();
		}
	}

	private boolean tradeFairy() { // Based on Ruski's ATTACKNPC method
		final RSNPC npc = getNearestNPCByName("Irksol");
		if (npc == null) {
			return false;
		}
		final RSTile tile = npc.getLocation();
		final RSTile randomTile = tile.randomizeTile(1, 1);
		try {
			final int hoverRand = random(8, 13);
			for (int i = 0; i < hoverRand; i++) {
				final Point screenLoc = npc.getScreenLocation();
				if ((!pointOnScreen(screenLoc) || interfaceFailed
						&& !movedTowards || findFails == 10)
						&& getMyPlayer().getInteracting() == null) {
					walkTileMM(randomTile);
					movedTowards = true;
					wait(random(3000, 5000));
					return false;
				}

				moveMouse(screenLoc, 15, 15);

				final List<String> menuItems = getMenuItems();
				if (menuItems.isEmpty() || menuItems.size() <= 1) {
					continue;
				}
				for (int a = 1; a < menuItems.size(); a++) {
					if (menuItems.get(a).toLowerCase().contains(
							npc.getName().toLowerCase())
							&& getMyPlayer().getInteracting() == null) {
						clickMouse(false);
						return atMenu("Trade");
					}
				}
			}

		} catch (final Exception e) {
			System.out.print("Error Trading Fairy: " + e);
			return false;
		}
		return false;
	}
}